import { PermissionAction } from '@supabase/shared-types/out/constants' import { IS_PLATFORM, useFeatureFlags, useFlag, useParams } from 'common' import dayjs, { Dayjs } from 'dayjs' import maxBy from 'lodash/maxBy' import meanBy from 'lodash/meanBy' import sumBy from 'lodash/sumBy' import { useRouter } from 'next/router' import { useMemo, useState } from 'react' import { Alert, AlertDescription, AlertTitle, Button, LogoLoader, WarningIcon } from 'ui' import { PageContainer } from 'ui-patterns/PageContainer' import { PageSection, PageSectionContent } from 'ui-patterns/PageSection' import { EdgeFunctionOverview } from '@/components/interfaces/Functions/EdgeFunctionOverview/EdgeFunctionOverview' import { EdgeFunctionRecentInvocations } from '@/components/interfaces/Functions/EdgeFunctionRecentInvocations' import ReportWidget from '@/components/interfaces/Reports/ReportWidget' import DefaultLayout from '@/components/layouts/DefaultLayout' import EdgeFunctionDetailsLayout from '@/components/layouts/EdgeFunctionsLayout/EdgeFunctionDetailsLayout' import AreaChart from '@/components/ui/Charts/AreaChart' import StackedBarChart from '@/components/ui/Charts/StackedBarChart' import NoPermission from '@/components/ui/NoPermission' import { FunctionsCombinedStatsVariables, useFunctionsCombinedStatsQuery, } from '@/data/analytics/functions-combined-stats-query' import { useEdgeFunctionQuery } from '@/data/edge-functions/edge-function-query' import { useFillTimeseriesSorted } from '@/hooks/analytics/useFillTimeseriesSorted' import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions' import type { ChartIntervals, NextPageWithLayout } from '@/types' const CHART_INTERVALS: ChartIntervals[] = [ { key: '15min', label: '15 min', startValue: 15, startUnit: 'minute', format: 'MMM D, h:mm:ssa', }, { key: '1hr', label: '1 hour', startValue: 1, startUnit: 'hour', format: 'MMM D, h:mma', }, { key: '3hr', label: '3 hours', startValue: 3, startUnit: 'hour', format: 'MMM D, h:mma', }, { key: '1day', label: '1 day', startValue: 1, startUnit: 'hour', format: 'MMM D, h:mma', }, ] const LegacyEdgeFunctionOverview = () => { const router = useRouter() const { ref: projectRef, functionSlug } = useParams() const [interval, setInterval] = useState('15min') const selectedInterval = CHART_INTERVALS.find((i) => i.key === interval) || CHART_INTERVALS[1] const { data: selectedFunction } = useEdgeFunctionQuery({ projectRef, slug: functionSlug, }) const id = selectedFunction?.id const combinedStatsResults = useFunctionsCombinedStatsQuery({ projectRef, functionId: id, interval: selectedInterval.key as FunctionsCombinedStatsVariables['interval'], }) const combinedStatsData = useMemo(() => { const result = combinedStatsResults.data?.result as | Record[] | undefined return result || [] }, [combinedStatsResults.data]) const [startDate, endDate]: [Dayjs, Dayjs] = useMemo(() => { const start = dayjs() .subtract(selectedInterval.startValue, selectedInterval.startUnit as dayjs.ManipulateType) .startOf(selectedInterval.startUnit as dayjs.ManipulateType) const end = dayjs().startOf(selectedInterval.startUnit as dayjs.ManipulateType) return [start, end] }, [selectedInterval]) const { data: combinedStatsChartData, error: combinedStatsError, isError: isErrorCombinedStats, } = useFillTimeseriesSorted({ data: combinedStatsData, timestampKey: 'timestamp', valueKey: [ 'requests_count', 'log_count', 'log_info_count', 'log_warn_count', 'log_error_count', 'success_count', 'redirect_count', 'client_err_count', 'server_err_count', 'avg_cpu_time_used', 'avg_memory_used', 'avg_execution_time', 'max_execution_time', 'avg_heap_memory_used', 'avg_external_memory_used', 'max_cpu_time_used', ], defaultValue: 0, startDate: startDate.toISOString(), endDate: endDate.toISOString(), }) const { isLoading: permissionsLoading, can: canReadFunction } = useAsyncCheckPermissions( PermissionAction.FUNCTIONS_READ, functionSlug as string ) if (!canReadFunction && !permissionsLoading) { return } return ( {IS_PLATFORM && id && (
)}
{CHART_INTERVALS.map((item, i) => { const classes = [] if (i === 0) { classes.push('rounded-tr-none rounded-br-none') } else if (i === CHART_INTERVALS.length - 1) { classes.push('rounded-tl-none rounded-bl-none') } else { classes.push('rounded-none') } return ( ) })}
Statistics for past {selectedInterval.label}
{ return isErrorCombinedStats ? ( Failed to reterieve execution time {combinedStatsError?.message ?? 'Unknown error'} ) : (
) }} /> { if (isErrorCombinedStats) { return ( Failed to reterieve invocations {combinedStatsError?.message ?? 'Unknown error'} ) } else { const requestData = props.data .map((d: any) => [ { status: '2xx', count: d.success_count, timestamp: d.timestamp, }, { status: '3xx', count: d.redirect_count, timestamp: d.timestamp, }, { status: '4xx', count: d.client_err_count, timestamp: d.timestamp, }, { status: '5xx', count: d.server_err_count, timestamp: d.timestamp, }, ]) .flat() const logsData = props.data .map((d: any) => [ { status: 'error', count: d.log_error_count, timestamp: d.timestamp, }, { status: 'info', count: d.log_info_count, timestamp: d.timestamp, }, { status: 'warn', count: d.log_warn_count, timestamp: d.timestamp, }, ]) .flat() return (
{ router.push( `/project/${projectRef}/functions/${functionSlug}/invocations?its=${startDate.toISOString()}` ) }} /> { router.push( `/project/${projectRef}/functions/${functionSlug}/logs?its=${startDate.toISOString()}` ) }} />
) } }} /> { return isErrorCombinedStats ? ( Failed to retrieve CPU time {combinedStatsError?.message ?? 'Unknown error'} ) : (
) }} /> { if (isErrorCombinedStats) { return ( Failed to retrieve memory usage {combinedStatsError?.message ?? 'Unknown error'} ) } const memoryData = props.data .map((d: any) => [ { type: 'heap', count: d.avg_heap_memory_used, timestamp: d.timestamp, }, { type: 'external', count: d.avg_external_memory_used, timestamp: d.timestamp, }, ]) .flat() return (
) }} />
) } const PageLayout: NextPageWithLayout = () => { const { hasLoaded: flagsLoaded } = useFeatureFlags() const showNewOverview = useFlag('edgeFunctionsOverview') === true if (IS_PLATFORM && !flagsLoaded) { return } if (showNewOverview) { return } return } PageLayout.getLayout = (page) => ( {page} ) export default PageLayout